﻿using System.DrawingCore;
using System.DrawingCore.Imaging;

namespace GoodAdmin.Core;

/// <summary>
/// 验证码功能
/// </summary>
public class CaptchaCodeGenerator
{
    /// <summary>
    /// 不同验证码类型对应字符
    /// </summary>
    private static readonly Dictionary<CaptchaType, List<char>> TypeCharactersMap = new()
    {
        {
            CaptchaType.DEFAULT,
            Characters.DEFAULT
        },
        {
            CaptchaType.CHINESE,
            Characters.CHINESE
        },
        {
            CaptchaType.NUMBER,
            Characters.NUMBER
        },
        {
            CaptchaType.NUMBER_ZH_CN,
            Characters.NUMBER_ZH_CN
        },
        {
            CaptchaType.NUMBER_ZH_HK,
            Characters.NUMBER_ZH_HK
        },
        {
            CaptchaType.WORD_NUMBER_LOWER,
            Characters.WORD_NUMBER_LOWER
        },
        {
            CaptchaType.WORD_NUMBER_UPPER,
            Characters.WORD_NUMBER_UPPER
        },
        {
            CaptchaType.WORD,
            Characters.WORD
        },
        {
            CaptchaType.WORD_LOWER,
            Characters.WORD_LOWER
        },
        {
            CaptchaType.WORD_UPPER,
            Characters.WORD_UPPER
        }
    };

    private static readonly Random random = new();        


    public static (string renderText, string code) GenerateCode(CaptchaType captchaType, int length)
    {
        if (captchaType == CaptchaType.ARITHMETIC)
        {
            return GenerateaArithmetic();
        }

        if (captchaType == CaptchaType.NUMBER_ZH_CN)
        {
            return GenerateaNumberZH(length, isHk: false);
        }

        if (captchaType == CaptchaType.NUMBER_ZH_HK)
        {
            return GenerateaNumberZH(length, isHk: true);
        }

        string text = Pick(TypeCharactersMap[captchaType], length);
        return (text, text);
    }

    /// <summary>
    /// 生成验证图像结果
    /// </summary>
    /// <param name="type"></param>
    /// <param name="length"></param>
    /// <param name="width"></param>
    /// <param name="heigh"></param>
    /// <param name="fontSize"></param>
    /// <returns></returns>
    public static CaptchaInfo GenerateImage(CaptchaType type = CaptchaType.DEFAULT, int length = 4, int width = 170, int heigh = 50, int fontSize = 20)
    {
        var (renderText, code) = GenerateCode(type, length);
        //颜色列表
        Color[] colors = { Color.Black, Color.Red, Color.Blue, Color.Green, Color.Orange, Color.Brown, Color.DarkBlue };
        //字体列表
        string[] fonts = { "Times New Roman", "Verdana", "Arial", "Gungsuh" };
        //创建画布
        Bitmap bitmap = new(width, heigh);
        Graphics graphics = Graphics.FromImage(bitmap);
        graphics.Clear(Color.White);
        Random random = new();
        //画躁线
        for (int i = 0; i < length; i++)
        {
            int x1 = random.Next(width);
            int y1 = random.Next(heigh);
            int x2 = random.Next(width);
            int y2 = random.Next(heigh);
            Color color = colors[random.Next(colors.Length)];
            Pen pen = new(color);
            graphics.DrawLine(pen, x1, y1, x2, y2);
        }
        //画噪点
        for (int i = 0; i < 100; i++)
        {
            int x = random.Next(width);
            int y = random.Next(heigh);
            Color color = colors[random.Next(colors.Length)];
            bitmap.SetPixel(x, y, color);
        }
        //画验证码
        for (int i = 0; i < length; i++)
        {
            string fontStr = fonts[random.Next(fonts.Length)];
            Font font = new(fontStr, fontSize);
            Color color = colors[random.Next(colors.Length)];
            //graphics.DrawString(charCode[i].ToString(), font, new SolidBrush(color), (float)i * 30 + 5, (float)0);
            graphics.DrawString(renderText[i].ToString(), font, new SolidBrush(color), (float)i * 40, (float)5);
        }
        //写入内存流
        try
        {
            MemoryStream stream = new();
            bitmap.Save(stream, ImageFormat.Jpeg);
            CaptchaInfo captchaInfo = new ()
            {
                Code = code,
                Image = stream.ToArray()
            };
            return captchaInfo;
        }
        //释放资源
        finally
        {
            graphics.Dispose();
            bitmap.Dispose();
        }
    }

    /// <summary>
    /// 生成汉字数字
    /// </summary>
    /// <param name="length">验证码长度</param>
    /// <param name="isHk">是否繁体</param>
    /// <returns></returns>
    private static (string renderText, string code) GenerateaNumberZH(int length, bool isHk)
    {
        StringBuilder stringBuilder = new();
        StringBuilder stringBuilder2 = new();
        List<char> list = (isHk ? Characters.NUMBER_ZH_HK : Characters.NUMBER_ZH_CN);
        for (int i = 0; i < length; i++)
        {
            int index = random.Next(list.Count);
            stringBuilder.Append(list[index]);
            stringBuilder2.Append(Characters.NUMBER[index]);
        }

        return (stringBuilder.ToString(), stringBuilder2.ToString());
    }

    /// <summary>
    /// 生成运算式
    /// </summary>
    /// <param name="length">算数个数</param>
    /// <returns></returns>
    private static (List<int> operands, List<char> operators) GenerateaArithmeticParts(int length)
    {
        List<int> operands = new();
        Enumerable.Range(0, length).ToList().ForEach(delegate
        {
            operands.Add(random.Next(10));
        });
        List<char> operators = new ();
        Enumerable.Range(0, length - 1).ToList().ForEach(delegate
        {
            switch (random.Next(3))
            {
                case 0:
                    operators.Add('+');
                    break;
                case 1:
                    operators.Add('-');
                    break;
                case 2:
                    operators.Add('x');
                    break;
            }
        });
        if (length == 2 && operators[0] == '-')
        {
            int value = Math.Max(operands[0], operands[1]);
            int value2 = Math.Min(operands[0], operands[1]);
            operands[0] = value;
            operands[1] = value2;
        }

        return (operands, operators);
    }

    /// <summary>
    /// 生成运算式
    /// </summary>
    /// <param name="length">算数个数</param>
    /// <returns></returns>
    private static (string renderText, string code) GenerateaArithmetic(int length)
    {
        (List<int> operands, List<char> operators) = GenerateaArithmeticParts(length);
        List<int> item = operands;
        List<char> item2 = operators;
        StringBuilder stringBuilder = new();
        stringBuilder.Append(item[0]);
        for (int i = 0; i < length - 1; i++)
        {
            stringBuilder.Append(item2[i]);
            stringBuilder.Append(item[i + 1]);
        }

        //string item3 = ((int)EvaluationEngine.Evaluate(stringBuilder.ToString())).ToString();
        return (stringBuilder.Append("=?").ToString(), "");
    }

    /// <summary>
    /// 生成运算式，只有2个算数
    /// </summary>
    /// <returns></returns>
    private static  (string renderText, string code) GenerateaArithmetic()
    {

        int first = random.Next(10);
        int second = random.Next(10);
        char operators;
        int res;
        switch (random.Next(3))
        {
            case 0:
                operators = '+';
                res = first + second;
                break;
            case 1:
                operators = '-';
                if (first < second)
                {
                    first ^= second;
                    second = first ^ second;
                    first ^= second;
                }
                res = first - second;
                break;
            default:
                operators = '*';
                res = first * second;
                break;
        }
        return ($"{first}{operators}{second}=?", res.ToString());
    }

    /// <summary>
    /// 生成随机字符
    /// </summary>
    /// <param name="characters"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    private static string Pick(List<char> characters, int count)
    {
        StringBuilder stringBuilder = new();
        for (int i = 0; i < count; i++)
        {
            stringBuilder.Append(characters[random.Next(characters.Count)]);
        }

        return stringBuilder.ToString();
    }
}